package Engine;

import Objects.GenericObject;
import Objects.MovableObject;
import Objects.ObjectPair;
import gleem.linalg.Vec3f;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

/**
 *
 * @author Gustavo Buzogany Eboli
 */
public class CollisionManager {

    private Map objects;
    private QuadTree quadTree;
    private ArrayList<ObjectPair> potentialCollisions;
    private float cullingRadius = 120;

    /**
     * Construtor do gerenciador de colisoes
     * @param objects lista de objetos
     * @param quadTree QuadTree a ser usada
     */
    public CollisionManager(TreeMap objects, QuadTree quadTree)
    {
        this.objects = objects;
        this.quadTree = quadTree;
    }

    /**
     * Gerencia os objetos que colidiram (executado sempre que objetos colidem)
     */
    public void handleObjectObjectCollisions() {
	potentialCollisions = new ArrayList<ObjectPair>();

	potentialObjectObjectCollisions();

        Iterator it = potentialCollisions.iterator();
        while (it.hasNext()) {
            ObjectPair op = (ObjectPair) it.next();
            MovableObject o1 = (MovableObject) op.o1;
            MovableObject o2 = (MovableObject) op.o2;
            if (testObjectObjectCollision(o1, o2)) {

                Vec3f displacement = o1.getPos().minus(o2.getPos());
                float massVar = 2*(o1.getMass()*o2.getMass()/(o1.getMass()+o2.getMass()));
                displacement.normalize();


		Vec3f delta = (o1.getPos().minus(o2.getPos()));
		float r = o1.getRadius() + o2.getRadius();
		float d = delta.length();

		Vec3f mtd;
		if (d != 0.0f)
		{
			mtd = delta.times(((o1.getRadius() + o2.getRadius())-d)/d); 
		}
		else 
		{
			d = o2.getRadius() + o1.getRadius() - 1.0f;
			delta = new Vec3f(o2.getRadius() + o1.getRadius(), 0.0f, 0.0f);

			mtd = delta.times(((o1.getRadius() + o2.getRadius())-d)/d);
		}


		float im1 = 1 / o1.getMass(); 
		float im2 = 1 / o2.getMass();

		o1.setPos(o1.getPos().plus(mtd.times(im1/(im1+im2))));
		o2.setPos(o2.getPos().minus(mtd.times(im2 / (im1 + im2))));

		Vec3f v = (o1.getSpeed().minus(o2.getSpeed()));
		float vn = v.dot(mtd.normalized());

		if (vn > 0.0f) return;

		float i = (-(1.0f + 0.85f) * vn) / (im1 + im2);
		Vec3f impulse = mtd.times(i);

                if(!(o1.getBotFlag() == true && o2.getBotFlag() == true))
                {
                    o1.decreaseHealth(i*200, 0);
                    o2.decreaseHealth(i*200, 0);
                }

		o1.setSpeed(o1.getSpeed().plus(impulse.times(im1)));
		o2.setSpeed(o2.getSpeed().minus(impulse.times(im2)));

            }
        }
    }

    /**
     * Testa os objetos para ver se estao colidindo
     * @param b1 Objeto 1
     * @param b2 Objeto 2
     * @return True se colidiu, falso caso contrario
     */
    private boolean testObjectObjectCollision(GenericObject b1, GenericObject b2) {

	float r = b1.getRadius() + b2.getRadius();

        float distance = b1.getPos().minus(b2.getPos()).lengthSquared();
        if(distance > 15000) {
            if(b1.getBotFlag() == true && b2.getPlayerFlag() == true)
            {
                b1.selfDestruct(true);
            } else if(b1.getPlayerFlag() == true && b2.getBotFlag() == true)
            {
                b2.selfDestruct(true);
            }
        }
        if (b1.getPlayerFlag() == true) {
            if (distance > cullingRadius * cullingRadius) {
                b2.setCullingVisible(false);
            } else {
                b2.setCullingVisible(true);
            }
        } else if (b2.getPlayerFlag() == true) {
            if (distance > cullingRadius * cullingRadius) {
                b1.setCullingVisible(false);
            } else {
                b1.setCullingVisible(true);
            }
        }
        if (distance < r * r) {
            return b1.collide(b2) && b2.collide(b1);
        } else {
            return false;
        }
    }

    /**
     * Verifica as colisoes em potencial
     */
    void potentialObjectObjectCollisions() {
	quadTree.potentialObjectObjectCollisions(potentialCollisions);
    }
}
